fix: ingesting one card no longer copies every card of its type#5289
fix: ingesting one card no longer copies every card of its type#5289jurgenwerk wants to merge 3 commits into
Conversation
…k graph
An instance URL meant "this record" but ingest-card swept in every instance of
the entry card's type (findEntryInstances searches `type: {module,name}`
regardless of entry kind) — so ingesting one WineCellar pulled both cellars +
all 8 bottles. A module URL still means "the card type."
Branch on entry kind in sync(): for an instance entry, crawl the same-realm
link graph from that instance (`linksTo`/`linksToMany` via relationship
`links.self`, transitively) — the instance analogue of crawlModules — instead
of the type-wide instance sweep. Module entries are unchanged.
Verified live: ingesting one catalog WineCellar instance now copies that cellar
+ its 6 linked bottles (was both cellars + all 8). New unit tests:
extractRelationshipLinks parsing + a fake-realm instance ingest that excludes
unrelated siblings.
CS-11682. Stacks on CS-11652 (per-realm _search) for Spec copying.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes boxel realm ingest-card <instance-url> so an instance entry copies only that specific record’s same-realm link graph (plus its module deps), instead of copying all instances of the card type.
Changes:
- Add
extractRelationshipLinks()and instance-link crawling (crawlInstanceLinks) to ingest same-realmrelationships.*.links.selftargets transitively when the entry is an instance URL. - Keep module-entry behavior unchanged (module URL still means “the type” → ingest all instances of that type).
- Add helper-unit coverage for relationship link extraction and a new integration-style fixture test for instance ingestion.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/boxel-cli/src/commands/realm/ingest-card.ts | Adds relationship-link extraction + BFS crawling for instance entries; branches ingest behavior based on entry kind. |
| packages/boxel-cli/tests/commands/ingest-card.test.ts | Adds unit tests for extractRelationshipLinks(). |
| packages/boxel-cli/tests/commands/ingest-card-instance.test.ts | Adds fixture-based test ensuring instance ingest includes only the entry + linked records (not unrelated siblings). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let rel = | ||
| /^https?:\/\//.test(self) || self.startsWith('@') | ||
| ? this.relativize(self) | ||
| : this.relativize(new URL(self, this.relToAbs(fromRel)).href); | ||
| let candidate = rel.endsWith('.json') ? rel : `${rel}.json`; | ||
| return fileSet.has(candidate) ? candidate : null; |
There was a problem hiding this comment.
(Written by Claude on Matic's behalf.)
Fixed in da9762f. resolveLinkedInstanceRel now resolves relative/absolute http(s) links to an absolute URL and requires startsWith(this.realmRoot) — a link into another realm (even one whose URL shares our path tail) returns null and is left as a runtime reference, mirroring resolveSameRealmFile. The published-alias @cardstack/<realm>/… form still maps by the realm tail. Added a regression case: the instance fixture's entry card now carries a decoy link to a different host with the same /garage/ tail pointing at a locally-existing path, and the test asserts it's not copied.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
resolveLinkedInstanceRel passed absolute http(s) links through relativize(), whose path-tail fallback could match another realm whose URL happens to share this realm's tail (e.g. a different host's /garage/), wrongly copying a local file. Require a resolved relative/absolute link to live under this realm's served root (mirrors resolveSameRealmFile); the published-alias (@cardstack/…) form still maps by tail. Adds a cross-realm decoy link to the instance test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What was wrong
boxel realm ingest-card <card>copies a card out of one realm into a local folder. When you pointed it at a single card instance, it copied every instance of that card's type instead of just the one you asked for — along with all the records those instances reference.Why it matters
The software factory uses this command to pull an existing card into a workspace so an agent can adjust it. Over-copying meant every "adjust a card" run dragged in unrelated records, which then bloated the baseline tests the factory writes and runs against the copy.
What this changes
The URL you give it now means what you'd expect:
Mechanically: for a single instance we now walk that card's links the same way we already walk a module's imports, instead of running a blanket "find everything of this type" search.
Testing
Note for reviewers
Builds on #5276 — without that change the card's catalog Spec isn't copied when the source realm is a shared/published one (a separate fix). The two land together.